home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 10 Scripting / 02 Berger / scc / CodeGen.cpp next >
Encoding:
C/C++ Source or Header  |  2001-10-16  |  12.2 KB  |  424 lines

  1. #include "SCC.H"
  2. #include "CodeGen.H"
  3. #include "PTNode.H"
  4.  
  5.  
  6. CodeGen::CodeGen()
  7. {
  8.   // When this class is instantiated, open the 'out.bin' file.  Obviously, a
  9.   // real compiler would want the user to specify the output file name as a
  10.   // command-line argument.
  11.   out = fopen( "out.bin", "wb" );
  12.   if ( out == NULL ) {
  13.     perror( "out.bin" );
  14.     exit( 1 );
  15.   }
  16. }
  17.  
  18.  
  19. CodeGen::~CodeGen()
  20. {
  21.   // Make sure to close the bytecode stream.
  22.   fclose( out );
  23. }
  24.  
  25.  
  26. void
  27. CodeGen::StartGen( PTNodePtr root )
  28. {
  29.   // Before we can start generating the code in the script, we have to
  30.   // allocate space for any variables that are used in the script.  Since the
  31.   // interpreter does not support scopes, all the variables exist at the base
  32.   // of the stack.  Iterate through the entire symbol table and allocate space
  33.   // for them on the stack.
  34.   //
  35.   // While we are doing this, we need to also need to set the variables
  36.   // address.  This will be important later when the variable is actually
  37.   // used.
  38.   SymbolTable::iterator i = symtbl.begin();
  39.   SymbolTable::iterator end = symtbl.end();
  40.  
  41.   unsigned int offset = 0;
  42.  
  43.   for ( ; i != end; ++i ) {
  44.     IdentifierNodePtr var = (*i).second;
  45.  
  46.     // The interpreter expects the variable offsets to be in word entries;
  47.     // therefore, we only need to increment the offset by one for each
  48.     // variable.
  49.     var->SetOffset( offset );
  50.     ++offset;
  51.  
  52.     // Emit a dummy push instruction.  This allocates space for the variable
  53.     // on the stack.
  54.     WriteOpcode( Push_Opcode );
  55.     WriteArg( 0 );
  56.   }
  57.  
  58.   // Now that space for all the variables have been allocated and their
  59.   // offsets are set, generate the script's code.
  60.   Gen( root );
  61.  
  62.   // To be clean, emit pop instructions to remove the variables that we
  63.   // previously added to the stack.  This will ensure that the stack starts
  64.   // and stops empty.
  65.   for ( i = symtbl.begin(); i != end; ++i )
  66.     WriteOpcode( Pop_Opcode );
  67. }
  68.  
  69.  
  70. size_t
  71. CodeGen::Gen( PTNodePtr node )
  72. {
  73.   // This function switches on the node's type and passes flow of control off
  74.   // to the proper internal code generator function.  This helper function
  75.   // will recursively call this function for any children that it may have.
  76.  
  77.   // It is useful for some of the opcode generators to know how much code has
  78.   // been generated.  Store a marker here when before we generate anything so
  79.   // we can figure out how many bytes was generated for this node.
  80.   Marker start = GetCurrentMarker();
  81.  
  82.   switch ( node->GetType() ) {
  83.     case Add_PTNodeType:
  84.       GenAdd( AddNodePtr( node ) );
  85.       break;
  86.  
  87.     case Subtract_PTNodeType:
  88.       GenSubtract( SubtractNodePtr( node ) );
  89.       break;
  90.  
  91.     case Multiply_PTNodeType:
  92.       GenMultiply( MultiplyNodePtr( node ) );
  93.       break;
  94.  
  95.     case Divide_PTNodeType:
  96.       GenDivide( DivideNodePtr( node ) );
  97.       break;
  98.  
  99.     case Constant_PTNodeType:
  100.       GenConstant( ConstantNodePtr( node ) );
  101.       break;
  102.  
  103.     case Identifier_PTNodeType:
  104.       GenIdentifier( IdentifierNodePtr( node ) );
  105.       break;
  106.  
  107.     case Assignment_PTNodeType:
  108.       GenAssignment( AssignmentNodePtr( node ) );
  109.       break;
  110.  
  111.     case If_PTNodeType:
  112.       GenIf( IfNodePtr( node ) );
  113.       break;
  114.  
  115.     case Statement_PTNodeType:
  116.       GenStatement( StatementNodePtr( node ) );
  117.       break;
  118.  
  119.     case Block_PTNodeType:
  120.       GenBlock( BlockNodePtr( node ) );
  121.       break;
  122.  
  123.     default:
  124.       // Some unknown node type!  This shouldn't happen...
  125.       assert( false );
  126.       break;
  127.   }
  128.  
  129.   return GetCurrentMarker() - start;
  130. }
  131.  
  132.  
  133. void
  134. CodeGen::GenAdd( AddNodePtr add )
  135. {
  136.   // All of the basic math nodes have the same format:  Generate the code for
  137.   // the left & right hand expressions, and then emit the proper opcode
  138.   // instruction.  This makes sure that the stack will contain the proper two
  139.   // values to preform the addition, subtraction, etc.
  140.   Gen( add->GetLHS() );
  141.   Gen( add->GetRHS() );
  142.  
  143.   WriteOpcode( Add_Opcode );
  144. }
  145.  
  146.  
  147. void
  148. CodeGen::GenSubtract( SubtractNodePtr subtract )
  149. {
  150.   Gen( subtract->GetLHS() );
  151.   Gen( subtract->GetRHS() );
  152.  
  153.   WriteOpcode( Subtract_Opcode );
  154. }
  155.  
  156.  
  157. void
  158. CodeGen::GenMultiply( MultiplyNodePtr multiply )
  159. {
  160.   Gen( multiply->GetLHS() );
  161.   Gen( multiply->GetRHS() );
  162.  
  163.   WriteOpcode( Multiply_Opcode );
  164. }
  165.  
  166.  
  167. void
  168. CodeGen::GenDivide( DivideNodePtr divide )
  169. {
  170.   Gen( divide->GetLHS() );
  171.   Gen( divide->GetRHS() );
  172.  
  173.   WriteOpcode( Divide_Opcode );
  174. }
  175.  
  176.  
  177. void
  178. CodeGen::GenConstant( ConstantNodePtr constant )
  179. {
  180.   // A constant simply pushes its own value onto the stack.  The Push opcode
  181.   // expects that the next word in the bytecode stream is the value to push
  182.   // onto the stack.
  183.   WriteOpcode( Push_Opcode );
  184.   WriteArg( constant->GetValue() );
  185. }
  186.  
  187.  
  188. void
  189. CodeGen::GenIdentifier( IdentifierNodePtr identifier )
  190. {
  191.   // An identifier node simply refers that the variable is being "used".  This
  192.   // means its current value should be pushed onto the stack.
  193.   WriteOpcode( Load_Opcode );
  194.   WriteArg( identifier->GetOffset() );
  195. }
  196.  
  197.  
  198. void
  199. CodeGen::GenAssignment( AssignmentNodePtr assignment )
  200. {
  201.   Gen( assignment->GetRHS() );
  202.  
  203.   // An assigment is an expression not a statement.  It needs to leave the
  204.   // result of the assignment on the stack.  This allows the script writer to
  205.   // chain assignments such as:  a = b = 5;
  206.   WriteOpcode( Dupe_Opcode );
  207.  
  208.   // The left hand side of the assignment node will always be the variable to
  209.   // assign to.  This node's code isn't emited directly.  It is only used to
  210.   // fetch the proper variable offset for the store opcode.
  211.   IdentifierNodePtr var = IdentifierNodePtr( assignment->GetLHS() );
  212.  
  213.   WriteOpcode( Store_Opcode );
  214.   WriteArg( var->GetOffset() );
  215. }
  216.  
  217.  
  218. void
  219. CodeGen::GenIf( IfNodePtr ifNode )
  220. {
  221.   // The assembly for an if-statement with an else block will look like:
  222.   //
  223.   //     Perform the conditional instructions.
  224.   //     If top-stack element is zero, Jump to label A.
  225.   //     Perform true statements.
  226.   //     Jump to label B.
  227.   // A:  Preform false statements.
  228.   // B:
  229.   //
  230.   // The last label, B, will actually point to the next instruction after
  231.   // the if.  An if-statement with out an else block is slightly simplier, it
  232.   // looks like:
  233.   //
  234.   //     Perform the conditional instructions.
  235.   //     If top-stack element is zero, Jump to label A.
  236.   //     Perform true statements.
  237.   // A:
  238.   //
  239.   // Note that the first assembly block will work for both forms of an if
  240.   // statement; however, the second form is slightly more efficient for
  241.   // if-statements with out a false block (it doesn't need the "Jump to B"
  242.   // instruction).
  243.  
  244.  
  245.   Marker ifArg;
  246.   size_t trueSize;
  247.  
  248.   // Generate the if-statement's conditional.  This will leave an argument on
  249.   // the stack that can be used by the IfZero opcode.
  250.   Gen( ifNode->GetConditional() );
  251.  
  252.   // Emit the IfZero opcode.  If the value on the stack is false, then this
  253.   // opcode will jump to the false statements of the if.  Since the size of
  254.   // the true block isn't known, save a marker so the value can be filled in
  255.   // later.
  256.   WriteOpcode( IfZero_Opcode );
  257.   ifArg = WriteArg( 0 );
  258.  
  259.   // Generate all of the true statements, and save their size.
  260.   trueSize = Gen( ifNode->GetTrueStatements() );
  261.  
  262.   // If this if-statement contains any false statements, then emit their code.
  263.   if ( ifNode->GetFalseStatements() != NULL ) {
  264.     Marker jumpArg;
  265.     size_t falseSize;
  266.  
  267.     // Before the false statements, a relative jump is required to skip these
  268.     // false statements.  This jump is considered part of the true block, and
  269.     // the true statement's size needs to be updated accordingly.
  270.     WriteOpcode( Jump_Opcode );
  271.     jumpArg = WriteArg( 0 );
  272.  
  273.     trueSize += sizeof( Opcode );
  274.     trueSize += sizeof( int );
  275.  
  276.     // Generate the if-statements, and update the Jump opcode's argument since
  277.     // we now know how many bytes it needs to skip.
  278.     falseSize = Gen( ifNode->GetFalseStatements() );
  279.     UpdateArg( jumpArg, falseSize );
  280.   }
  281.  
  282.   // Now that the finial size of the true statement block is known, update
  283.   // IfZero's argument.
  284.   UpdateArg( ifArg, trueSize );
  285. }
  286.  
  287.  
  288. void
  289. CodeGen::GenFor( ForNodePtr forStmt )
  290. {
  291.   // The assembly for a for-statement looks like:
  292.   //
  293.   //     Perform pre-loop expression.
  294.   // A:  Perform loop conditional expression.
  295.   //     Jump to label B if top-stack element is zero.
  296.   //     Perform loop body statements.
  297.   //     Perform loop incremental expression.
  298.   //     Jump to label A.
  299.   // B:
  300.   //
  301.   // The last label, B, will actually point to the next isntruction after the
  302.   // for.
  303.  
  304.   Marker loopTop;
  305.   Marker loopSize;
  306.   Marker ifArg;
  307.   Marker jumpArg;
  308.  
  309.   // Generate the code for the preloop expression.  Nothing special needs to
  310.   // happen here.
  311.   Gen( forStmt->GetPreLoop() );
  312.  
  313.   // Save a marker to the top of the loop.  This will be needed to calculate
  314.   // the size of the loop body.
  315.   loopTop = GetCurrentMarker();
  316.  
  317.   // Generate the conditional part of the for-loop.  Note that we have to
  318.   // generate the IfZero opcodes to actually test the conditional and jump out
  319.   // of the loop, if needed.
  320.   Gen( forStmt->GetConditional() );
  321.   WriteOpcode( IfZero_Opcode );
  322.   ifArg = WriteArg( 0 );
  323.  
  324.   // Generate the code for the inner part of the function loop (this includes
  325.   // the incremental expression).
  326.   Gen( forStmt->GetLoopBody() );
  327.   Gen( forStmt->GetPostLoop() );
  328.  
  329.   // Generate the code to jump back up to the conditional.
  330.   WriteOpcode( Jump_Opcode );
  331.   jumpArg = WriteArg( 0 );
  332.  
  333.   // Now that the entire body of the loop has been generated, calculate the
  334.   // size of the loop body so we can update the jump values.  Note that the
  335.   // jump at the end of the loop needs to go backwards (hence the negative).
  336.   loopSize = GetCurrentMarker() - loopTop;
  337.  
  338.   UpdateArg( ifArg, loopSize );
  339.   UpdateArg( jumpArg, -loopSize );
  340. }
  341.  
  342.  
  343. void
  344. CodeGen::GenStatement( StatementNodePtr stmt )
  345. {
  346.   // A statement yields its entire work to its expression; however, this
  347.   // expression will leave a value on the stack.  For example, the expression
  348.   // "4+5" will leave the answer "9" as the top-most stack element.  Since
  349.   // this statemnt has been completed, it is save to pop this element off the
  350.   // stack.  It is no longer needed.
  351.   //
  352.   // HOWEVER, a statement with no expression does not generate any code (since
  353.   // there is nothing to pop off).  This makes it easy to support empty
  354.   // statements (most notiably for a for-loop).
  355.   if ( stmt == NULL )
  356.     return;
  357.  
  358.   Gen( stmt->GetExpression() );
  359.  
  360.   WriteOpcode( Pop_Opcode );
  361. }
  362.  
  363.  
  364. void
  365. CodeGen::GenBlock( BlockNodePtr block )
  366. {
  367.   // Block nodes do not have any code to generate.  They simply iterate over
  368.   // all their children making sure that their values are generated.
  369.   NodeList::const_iterator i = block->GetChildrenBegin();
  370.   NodeList::const_iterator end = block->GetChildrenEnd();
  371.  
  372.   for ( ; i != end; ++i ) {
  373.     const PTNodePtr node = *i;
  374.  
  375.     Gen( node );
  376.   }
  377. }
  378.  
  379.  
  380. void
  381. CodeGen::WriteOpcode( Opcode op )
  382. {
  383.   // Write out opcodes as if they were an integer.  If the size of the
  384.   // generate bytecode stream is an issue, then you'll want to only
  385.   // write out the smallest amount possible (probably a byte).
  386.   WriteArg( (int)op );
  387. }
  388.  
  389.  
  390. CodeGen::Marker
  391. CodeGen::WriteArg( int arg )
  392. {
  393.   // In case the code generator needs to update this opcode argument, return
  394.   // this argument's marker.  We have to fetch the marker now before we update
  395.   // the output file since the marker is supposed to point to the beginning of
  396.   // the argument.
  397.   Marker pos = GetCurrentMarker();
  398.  
  399.   // Some opcodes (such as Push) expect an argument from the bytecode stream.
  400.   // This function writes out such and argument to the output file.
  401.   fwrite( &arg, sizeof( arg ), 1, out );
  402.  
  403.   return pos;
  404. }
  405.  
  406.  
  407. void
  408. CodeGen::UpdateArg( Marker pos, int arg )
  409. {
  410.   // The marker is really just a file position, so rewind back to the file
  411.   // position, write the new argument, and zip back to the end of the file.
  412.   fseek( out, pos, SEEK_SET );
  413.   WriteArg( arg );
  414.   fseek( out, 0, SEEK_END );
  415. }
  416.  
  417.  
  418. CodeGen::Marker
  419. CodeGen::GetCurrentMarker()
  420. {
  421.   // Just use the current offset in the output file as the marker.
  422.   return ftell( out );
  423. }
  424.